{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### What is a Module?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A module is simply another data type. And the modules we use are instances of that data type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That word `math` is simply a label (think variable name) in our (global) namespace that points to some object in memory that is the `math` module."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's see what is in our global namespace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'In': ['', 'import math', 'globals()'],\n",
       " 'Out': {},\n",
       " '_': '',\n",
       " '__': '',\n",
       " '___': '',\n",
       " '__builtin__': <module 'builtins' (built-in)>,\n",
       " '__builtins__': <module 'builtins' (built-in)>,\n",
       " '__doc__': 'Automatically created module for IPython interactive environment',\n",
       " '__loader__': None,\n",
       " '__name__': '__main__',\n",
       " '__package__': None,\n",
       " '__spec__': None,\n",
       " '_dh': ['d:\\\\fbapt\\\\Dropbox\\\\Python Deep Dive\\\\Section 09 - Modules, Packages and Namespaces\\\\02 - What is a Module'],\n",
       " '_i': 'import math',\n",
       " '_i1': 'import math',\n",
       " '_i2': 'globals()',\n",
       " '_ih': ['', 'import math', 'globals()'],\n",
       " '_ii': '',\n",
       " '_iii': '',\n",
       " '_oh': {},\n",
       " 'exit': <IPython.core.autocall.ZMQExitAutocall at 0x1ae10cb5550>,\n",
       " 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x000001AE10373208>>,\n",
       " 'math': <module 'math' (built-in)>,\n",
       " 'quit': <IPython.core.autocall.ZMQExitAutocall at 0x1ae10cb5550>}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "globals()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'math' (built-in)>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "globals()['math']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "module"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'math' (built-in)>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's just an object of type `module`, and it even has a memory address:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1847086390312"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(math)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Take note of this memory address, we'll want to refer to it later!\n",
    "\n",
    "Let me show you what happens if I set the `math` **label** to `None` (I could even use `del globals()['math']`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "math = None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "NoneType"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1800367120"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(math)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see the label `math` now points to something else.\n",
    "\n",
    "Let me re-import it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we can see:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'math' (built-in)>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1847086390312"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(math)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll notice that the label `math` now is the **same** memory address as the first time we ran the import."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NOTE**: Please do not do this in your code. You never what side effects you may encounter - I just showed you this to make a point - when I ran the import the second time, I obtained a label that pointed to the **same** object."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What happens is that when you import a module, it is not actually loaded into the module's namespace only. Instead, the module is loaded into an overarching global system dictionary that contains the module name and the reference to the module object. The name we see here is \"copied\" into our namespace from that system namespace."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we had a project with multiple modules that each imported `math`, Python will load the `math` module the first time it is requested and put it into memory.\n",
    "\n",
    "The next time the `math` module is imported (in some different module), Python always looks at the system modules first - if it is there it simply copies that reference into our module's namespace and sets the label accordingly."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take a look at the system modules:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import sys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(sys.modules)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `sys.modules` currently contains a **lot** of entries, so I'm just going to look at the one we're interested in - the `math` module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'math' (built-in)>"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sys.modules['math']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aha! The `sys.modules` dictionary contains a key for `math` and as you saw it is the `math` module. In fact we can look at the memory address once more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1847086390312"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(sys.modules['math'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compare that to the `id` of the `math` module in our own (main) module - the same!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have established that a module is just an instance of the `module` type, and where it lives (in memory) with references to it maintained in the `sys.modules` dictionary as well as in any module namespace that imported it, let's see how we could create a module dynamically!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If it's an object, let's inspect it..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'math'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math.__name__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__doc__': 'This module is always available.  It provides access to the\\nmathematical functions defined by the C standard.',\n",
       " '__loader__': _frozen_importlib.BuiltinImporter,\n",
       " '__name__': 'math',\n",
       " '__package__': '',\n",
       " '__spec__': ModuleSpec(name='math', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'),\n",
       " 'acos': <function math.acos>,\n",
       " 'acosh': <function math.acosh>,\n",
       " 'asin': <function math.asin>,\n",
       " 'asinh': <function math.asinh>,\n",
       " 'atan': <function math.atan>,\n",
       " 'atan2': <function math.atan2>,\n",
       " 'atanh': <function math.atanh>,\n",
       " 'ceil': <function math.ceil>,\n",
       " 'copysign': <function math.copysign>,\n",
       " 'cos': <function math.cos>,\n",
       " 'cosh': <function math.cosh>,\n",
       " 'degrees': <function math.degrees>,\n",
       " 'e': 2.718281828459045,\n",
       " 'erf': <function math.erf>,\n",
       " 'erfc': <function math.erfc>,\n",
       " 'exp': <function math.exp>,\n",
       " 'expm1': <function math.expm1>,\n",
       " 'fabs': <function math.fabs>,\n",
       " 'factorial': <function math.factorial>,\n",
       " 'floor': <function math.floor>,\n",
       " 'fmod': <function math.fmod>,\n",
       " 'frexp': <function math.frexp>,\n",
       " 'fsum': <function math.fsum>,\n",
       " 'gamma': <function math.gamma>,\n",
       " 'gcd': <function math.gcd>,\n",
       " 'hypot': <function math.hypot>,\n",
       " 'inf': inf,\n",
       " 'isclose': <function math.isclose>,\n",
       " 'isfinite': <function math.isfinite>,\n",
       " 'isinf': <function math.isinf>,\n",
       " 'isnan': <function math.isnan>,\n",
       " 'ldexp': <function math.ldexp>,\n",
       " 'lgamma': <function math.lgamma>,\n",
       " 'log': <function math.log>,\n",
       " 'log10': <function math.log10>,\n",
       " 'log1p': <function math.log1p>,\n",
       " 'log2': <function math.log2>,\n",
       " 'modf': <function math.modf>,\n",
       " 'nan': nan,\n",
       " 'pi': 3.141592653589793,\n",
       " 'pow': <function math.pow>,\n",
       " 'radians': <function math.radians>,\n",
       " 'sin': <function math.sin>,\n",
       " 'sinh': <function math.sinh>,\n",
       " 'sqrt': <function math.sqrt>,\n",
       " 'tan': <function math.tan>,\n",
       " 'tanh': <function math.tanh>,\n",
       " 'tau': 6.283185307179586,\n",
       " 'trunc': <function math.trunc>}"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math.__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how all the methods and \"constants\" (such as pi) are just members of a dictionary with values being functions or values:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "math.sqrt is math.__dict__['sqrt']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, when we write `math.sqrt` we are basically just retrieving the function stored in the `math.__dict__` dictionary at that key (`sqrt`)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now the `math` module is a little special - it is written in C and actually a built-in.\n",
    "\n",
    "Let's look at another module from the standard library:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "import fractions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Decimal': decimal.Decimal,\n",
       " 'Fraction': fractions.Fraction,\n",
       " '_PyHASH_INF': 314159,\n",
       " '_PyHASH_MODULUS': 2305843009213693951,\n",
       " '_RATIONAL_FORMAT': re.compile(r'\\n    \\A\\s*                      # optional whitespace at the start, then\\n    (?P<sign>[-+]?)            # an optional sign, then\\n    (?=\\d|\\.\\d)                # lookahead for digit or .digit\\n    (?P<num>\\d*)               # numerator (possibly empty)\\n    (?:                        # followed by\\n       (?:/(?P<denom>\\d+))?    # an optional denominator\\n    |                          # or\\n       (?:\\.(?P<decimal>\\d*))? # an optional fractional part\\n       (?:E(?P<exp>[-+]?\\d+))? # and optional exponent\\n    )\\n    \\s*\\Z                      # and optional whitespace to finish\\n',\n",
       " re.IGNORECASE|re.UNICODE|re.VERBOSE),\n",
       " '__all__': ['Fraction', 'gcd'],\n",
       " '__builtins__': {'ArithmeticError': ArithmeticError,\n",
       "  'AssertionError': AssertionError,\n",
       "  'AttributeError': AttributeError,\n",
       "  'BaseException': BaseException,\n",
       "  'BlockingIOError': BlockingIOError,\n",
       "  'BrokenPipeError': BrokenPipeError,\n",
       "  'BufferError': BufferError,\n",
       "  'BytesWarning': BytesWarning,\n",
       "  'ChildProcessError': ChildProcessError,\n",
       "  'ConnectionAbortedError': ConnectionAbortedError,\n",
       "  'ConnectionError': ConnectionError,\n",
       "  'ConnectionRefusedError': ConnectionRefusedError,\n",
       "  'ConnectionResetError': ConnectionResetError,\n",
       "  'DeprecationWarning': DeprecationWarning,\n",
       "  'EOFError': EOFError,\n",
       "  'Ellipsis': Ellipsis,\n",
       "  'EnvironmentError': OSError,\n",
       "  'Exception': Exception,\n",
       "  'False': False,\n",
       "  'FileExistsError': FileExistsError,\n",
       "  'FileNotFoundError': FileNotFoundError,\n",
       "  'FloatingPointError': FloatingPointError,\n",
       "  'FutureWarning': FutureWarning,\n",
       "  'GeneratorExit': GeneratorExit,\n",
       "  'IOError': OSError,\n",
       "  'ImportError': ImportError,\n",
       "  'ImportWarning': ImportWarning,\n",
       "  'IndentationError': IndentationError,\n",
       "  'IndexError': IndexError,\n",
       "  'InterruptedError': InterruptedError,\n",
       "  'IsADirectoryError': IsADirectoryError,\n",
       "  'KeyError': KeyError,\n",
       "  'KeyboardInterrupt': KeyboardInterrupt,\n",
       "  'LookupError': LookupError,\n",
       "  'MemoryError': MemoryError,\n",
       "  'ModuleNotFoundError': ModuleNotFoundError,\n",
       "  'NameError': NameError,\n",
       "  'None': None,\n",
       "  'NotADirectoryError': NotADirectoryError,\n",
       "  'NotImplemented': NotImplemented,\n",
       "  'NotImplementedError': NotImplementedError,\n",
       "  'OSError': OSError,\n",
       "  'OverflowError': OverflowError,\n",
       "  'PendingDeprecationWarning': PendingDeprecationWarning,\n",
       "  'PermissionError': PermissionError,\n",
       "  'ProcessLookupError': ProcessLookupError,\n",
       "  'RecursionError': RecursionError,\n",
       "  'ReferenceError': ReferenceError,\n",
       "  'ResourceWarning': ResourceWarning,\n",
       "  'RuntimeError': RuntimeError,\n",
       "  'RuntimeWarning': RuntimeWarning,\n",
       "  'StopAsyncIteration': StopAsyncIteration,\n",
       "  'StopIteration': StopIteration,\n",
       "  'SyntaxError': SyntaxError,\n",
       "  'SyntaxWarning': SyntaxWarning,\n",
       "  'SystemError': SystemError,\n",
       "  'SystemExit': SystemExit,\n",
       "  'TabError': TabError,\n",
       "  'TimeoutError': TimeoutError,\n",
       "  'True': True,\n",
       "  'TypeError': TypeError,\n",
       "  'UnboundLocalError': UnboundLocalError,\n",
       "  'UnicodeDecodeError': UnicodeDecodeError,\n",
       "  'UnicodeEncodeError': UnicodeEncodeError,\n",
       "  'UnicodeError': UnicodeError,\n",
       "  'UnicodeTranslateError': UnicodeTranslateError,\n",
       "  'UnicodeWarning': UnicodeWarning,\n",
       "  'UserWarning': UserWarning,\n",
       "  'ValueError': ValueError,\n",
       "  'Warning': Warning,\n",
       "  'WindowsError': OSError,\n",
       "  'ZeroDivisionError': ZeroDivisionError,\n",
       "  '__IPYTHON__': True,\n",
       "  '__build_class__': <function __build_class__>,\n",
       "  '__debug__': True,\n",
       "  '__doc__': \"Built-in functions, exceptions, and other objects.\\n\\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.\",\n",
       "  '__import__': <function __import__>,\n",
       "  '__loader__': _frozen_importlib.BuiltinImporter,\n",
       "  '__name__': 'builtins',\n",
       "  '__package__': '',\n",
       "  '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>),\n",
       "  'abs': <function abs>,\n",
       "  'all': <function all>,\n",
       "  'any': <function any>,\n",
       "  'ascii': <function ascii>,\n",
       "  'bin': <function bin>,\n",
       "  'bool': bool,\n",
       "  'bytearray': bytearray,\n",
       "  'bytes': bytes,\n",
       "  'callable': <function callable>,\n",
       "  'chr': <function chr>,\n",
       "  'classmethod': classmethod,\n",
       "  'compile': <function compile>,\n",
       "  'complex': complex,\n",
       "  'copyright': Copyright (c) 2001-2017 Python Software Foundation.\n",
       "  All Rights Reserved.\n",
       "  \n",
       "  Copyright (c) 2000 BeOpen.com.\n",
       "  All Rights Reserved.\n",
       "  \n",
       "  Copyright (c) 1995-2001 Corporation for National Research Initiatives.\n",
       "  All Rights Reserved.\n",
       "  \n",
       "  Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.\n",
       "  All Rights Reserved.,\n",
       "  'credits':     Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands\n",
       "      for supporting Python development.  See www.python.org for more information.,\n",
       "  'delattr': <function delattr>,\n",
       "  'dict': dict,\n",
       "  'dir': <function dir>,\n",
       "  'display': <function IPython.core.display.display>,\n",
       "  'divmod': <function divmod>,\n",
       "  'enumerate': enumerate,\n",
       "  'eval': <function eval>,\n",
       "  'exec': <function exec>,\n",
       "  'filter': filter,\n",
       "  'float': float,\n",
       "  'format': <function format>,\n",
       "  'frozenset': frozenset,\n",
       "  'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x000001AE10373208>>,\n",
       "  'getattr': <function getattr>,\n",
       "  'globals': <function globals>,\n",
       "  'hasattr': <function hasattr>,\n",
       "  'hash': <function hash>,\n",
       "  'help': Type help() for interactive help, or help(object) for help about object.,\n",
       "  'hex': <function hex>,\n",
       "  'id': <function id>,\n",
       "  'input': <bound method Kernel.raw_input of <ipykernel.ipkernel.IPythonKernel object at 0x000001AE10352EB8>>,\n",
       "  'int': int,\n",
       "  'isinstance': <function isinstance>,\n",
       "  'issubclass': <function issubclass>,\n",
       "  'iter': <function iter>,\n",
       "  'len': <function len>,\n",
       "  'license': See https://www.python.org/psf/license/,\n",
       "  'list': list,\n",
       "  'locals': <function locals>,\n",
       "  'map': map,\n",
       "  'max': <function max>,\n",
       "  'memoryview': memoryview,\n",
       "  'min': <function min>,\n",
       "  'next': <function next>,\n",
       "  'object': object,\n",
       "  'oct': <function oct>,\n",
       "  'open': <function io.open>,\n",
       "  'ord': <function ord>,\n",
       "  'pow': <function pow>,\n",
       "  'print': <function print>,\n",
       "  'property': property,\n",
       "  'range': range,\n",
       "  'repr': <function repr>,\n",
       "  'reversed': reversed,\n",
       "  'round': <function round>,\n",
       "  'set': set,\n",
       "  'setattr': <function setattr>,\n",
       "  'slice': slice,\n",
       "  'sorted': <function sorted>,\n",
       "  'staticmethod': staticmethod,\n",
       "  'str': str,\n",
       "  'sum': <function sum>,\n",
       "  'super': super,\n",
       "  'tuple': tuple,\n",
       "  'type': type,\n",
       "  'vars': <function vars>,\n",
       "  'zip': zip},\n",
       " '__cached__': 'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\__pycache__\\\\fractions.cpython-36.pyc',\n",
       " '__doc__': 'Fraction, infinite-precision, real numbers.',\n",
       " '__file__': 'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\fractions.py',\n",
       " '__loader__': <_frozen_importlib_external.SourceFileLoader at 0x1ae10e4cf98>,\n",
       " '__name__': 'fractions',\n",
       " '__package__': '',\n",
       " '__spec__': ModuleSpec(name='fractions', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000001AE10E4CF98>, origin='D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\fractions.py'),\n",
       " '_gcd': <function fractions._gcd>,\n",
       " 'gcd': <function fractions.gcd>,\n",
       " 'math': <module 'math' (built-in)>,\n",
       " 'numbers': <module 'numbers' from 'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\numbers.py'>,\n",
       " 'operator': <module 'operator' from 'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\operator.py'>,\n",
       " 're': <module 're' from 'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\re.py'>,\n",
       " 'sys': <module 'sys' (built-in)>}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fractions.__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice a few properties here that look interesting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'D:\\\\Users\\\\fbapt\\\\Anaconda3\\\\envs\\\\deepdive\\\\lib\\\\fractions.py'"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fractions.__file__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's where the `fractions` module source code resides. I am using a virtual environment (conda), and the module `fractions.py` resides in that directory."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So a module is an object that is:\n",
    "- loaded from file (maybe! we'll see that in a second)\n",
    "- has a namespace\n",
    "- is a container of global variables (that `__dict__` we saw)\n",
    "- is an execution environment (we'll see that in an upcoming video)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, modules are just specific data types, and like any other data type in Python (think classes, functions, etc) we can create them dynamically - they do not have to be loaded from file (though that is how we do it most of the time)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import types"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(fractions, types.ModuleType)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, modules are instances of the `ModuleType` class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on class module in module builtins:\n",
      "\n",
      "class module(object)\n",
      " |  module(name[, doc])\n",
      " |  \n",
      " |  Create a module object.\n",
      " |  The name must be a string; the optional doc argument can have any type.\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __dir__(...)\n",
      " |      __dir__() -> list\n",
      " |      specialized dir() implementation\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __init__(self, /, *args, **kwargs)\n",
      " |      Initialize self.  See help(type(self)) for accurate signature.\n",
      " |  \n",
      " |  __new__(*args, **kwargs) from builtins.type\n",
      " |      Create and return a new object.  See help(type) for accurate signature.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors defined here:\n",
      " |  \n",
      " |  __dict__\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(ModuleType)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's go ahead and create a new module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "mod = types.ModuleType('point', 'A module for handling points.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<module 'point'>"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on module point:\n",
      "\n",
      "NAME\n",
      "    point - A module for handling points.\n",
      "\n",
      "FILE\n",
      "    (built-in)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(mod)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OK, so now let's add some functionality to it by simply setting some attributes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple\n",
    "mod.Point = namedtuple('Point', 'x y')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def points_distance(pt1, pt2):\n",
    "    return math.sqrt((pt1.x - pt2.x) ** 2 + (pt1.y - pt2.y) ** 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "mod.distance = points_distance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'Point': __main__.Point,\n",
       " '__doc__': 'A module for handling points.',\n",
       " '__loader__': None,\n",
       " '__name__': 'point',\n",
       " '__package__': None,\n",
       " '__spec__': None,\n",
       " 'distance': <function __main__.points_distance>}"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "p1 = mod.Point(0, 0)\n",
    "p2 = mod.Point(1, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.4142135623730951"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod.distance(p1, p2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see it behaves just like an ordinary module.\n",
    "\n",
    "However, one major difference here is that it is not located in the `sys.modules` dictionary - so another module in our program would not know anything about it.\n",
    "\n",
    "But we can fix that! We'll see this in one of the next videos.\n",
    "\n",
    "But first we'll need to take a peek at how Python imports a module from file. COming right up!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
